#! /usr/bin/env ruby

require 'optparse'

$opts = {
  :k => 70,
  :mk => 17,
  :ppb => 2, # points per base
  :lmargin => 50, # top margin
  :tmargin => 50, # left margin
  :space => 20, # space between unitigs
}

parser = OptionParser.new do |o|
  o.banner = "Usage: $0 kUnitigsLengths super_read [PacBio_read details_file]"

  o.on("-k", "Length of k-mer for unitigs") { |v|
    $opts[:k] = v.to_i
  }

  o.on("-m", "--mk", "Length of k-mer for matching") { |v|
    $opts[:mk] = v.to_i
  }

  o.on("--ppb", "Points per base (#{$opts[:ppb]})") { |v|
    $opts[:ppb] = v.to_i
  }

  o.on("-h", "--help", "This message") {
    puts(o)
    exit(0)
  }
end
parser.parse!

lengths_file = ARGV.shift
super_read   = ARGV.shift
if lengths_file.nil? || super_read.nil?
  STDERR.puts(parser)
  exit(1)
end

lengths = {}
open(lengths_file) { |fd|
  fd.each_line { |l|
    name, length = l.split
    lengths[name] = length.to_i
  }
}

fontsize = 14

$extra_offset = 0
$kmers_offset = 0

$max_x = $min_x = nil
$max_y = $min_y = nil

def xc x
  res = $opts[:lmargin] + $opts[:ppb] * ($kmers_offset + x)
  $max_x = res if $max_x.nil? || $max_x < res
  $min_x = res if $min_x.nil? || $min_x > res
  res
end
def yc i
  res = $extra_offset + $opts[:tmargin] + i * $opts[:space]
  $max_y = res if $max_y.nil? || $max_y < res
  $min_y = res if $min_y.nil? || $min_y > res
  res
end
def marker ori; ori ? "marker-end=\"url(#Arrow1Mend)\"" : "marker-start=\"url(#Arrow1Mstart)\""; end

vertical_lines = ""
kmer_lines = ""
match_lines = ""
sr_lines = ""

# A pacbio read is given with details of maching. Display all the k-mers
$kmers_base = $opts[:tmargin]
if ARGV.size >= 2
  pacbio_read = ARGV.shift
  details_file = ARGV.shift

  open(details_file) { |fd|
    fd.each_line { |l|
      pb, sr, rest = l.split(' ', 3)
      next unless pb == pacbio_read && sr == super_read

      $kmers_base += $opts[:space]
      
      matches = rest.split.map { |m|
        m =~ /^(\[)?(\d+):([\d-]+)\]?$/
        [$1, $2.to_i, $3.to_i.abs, $3.to_i > 0]
      }.sort_by { |s, pb_x, sr_x| sr_x }
      pb_matches = matches.minmax_by { |x, pb_x, sr_x| pb_x }
      pb_matches = [pb_matches[0][1], pb_matches[1][1]]

      # Shift matches based on location of most matches
      sr_min = matches.min_by { |s, pb_x, sr_x, ori| s ? sr_x : 1_000_000_000 }
      sr_max = matches.max_by { |s, pb_x, sr_x, ori| s ? sr_x : 0 }
      barycenter = matches.inject([0, 0]) { |a, (s, pb_x, sr_x, ori)| s ? [a[0] + 1, a[1] + pb_x] : a }
      barycenter = (barycenter[1].to_f / barycenter[0]).round
      $kmers_offset = barycenter - ((sr_max[2] - sr_min[2]).abs) / 2 - pb_matches[0] - sr_min[2].abs
      
      prev_top = -$opts[:mk] # prev k-mer drawn at the top
      yoff = $opts[:space] - 4 # yoff for the previous k-mer draw
      max_yoff = 0;
      match_id = 0
      matches.each { |selected, pb_x, sr_x, ori|
        if sr_x > prev_top + $opts[:mk]
          yoff = $opts[:space]
          prev_top = sr_x
        else
          yoff += 4
        end
        max_yoff = yoff if yoff > max_yoff
        stroke = selected ? "stroke=\"red\"" : "stroke=\"black\""
        match_lines += "<path d=\"M#{xc(sr_x)} #{$opts[:tmargin] + yoff} L#{xc(pb_x - pb_matches[0] - $kmers_offset)} #{$opts[:tmargin] + 2}\" stroke=\"lightgrey\" opacity=\"0.5\"> <set attributeName=\"opacity\" from=\"0.5\" to=\"1\" begin=\"match_#{match_id}.mouseover\" end=\"match_#{match_id}.mouseout\"/> </path>\n"
        match_lines += "<text visibility=\"hidden\" text-anchor=\"middle\" font-size=\"50%\" x=\"#{xc(pb_x - pb_matches[0] - $kmers_offset)}\" y=\"#{$opts[:tmargin]}\">#{pb_x} <set attributeName=\"visibility\" from=\"hidden\" to=\"visible\" begin=\"match_#{match_id}.mouseover\" end=\"match_#{match_id}.mouseout\" /> </text>\n"
        kmer_lines += "<path id=\"match_#{match_id}\" d=\"M#{xc(sr_x)} #{$opts[:tmargin] + yoff} l#{$opts[:mk]} 0\" #{stroke} #{marker(ori)}/>\n"
        match_id += 1
      }
      $extra_offset = max_yoff + $opts[:space]

      # draw pac bio read line
      pb_len = pb_matches[1] - pb_matches[0] + 1
      match_lines += "<path d=\"M#{xc(0 - $kmers_offset)} #{$opts[:tmargin] + 2} L#{xc(pb_len - $kmers_offset)} #{$opts[:tmargin] + 2}\" stroke=\"black\" />\n"
      match_lines += "<text x=\"#{xc(0 - $kmers_offset)}\" y=\"#{$opts[:tmargin]}\" text-anchor=\"middle\" font-size=\"50%\">#{pb_matches[0]}</text>\n"
      match_lines += "<text x=\"#{xc(pb_len - $kmers_offset)}\" y=\"#{$opts[:tmargin]}\" text-anchor=\"middle\" font-size=\"50%\">#{pb_matches[1]}</text>\n"    

      break
    }
  }
end

unitigs = super_read.split(/_/)

cstart = cend = nil # Start and end of contig in pixel
unitigs.each_with_index { |ur, i|
  unitig = ur[0..-2]
  ori    = ur[-1]
  len    = lengths[unitig]
  if !len
    puts("<text x=\"#{lmargin}\" y=\"#{yc(i)}\" color=\"red\">Unknown unitig #{unitig}</text>") if iteration == 0
    next
  end
  
  if cend
    cend  += len - $opts[:k] + 1
    cstart = cend - len + 1
  else
    cstart = 1
    cend   = len
  end
  
  vertical_lines += "<path d=\"M#{xc(cstart)} #{yc(i)} L#{xc(cstart)} #{$kmers_base}\" stroke=\"lightgrey\" />\n"
  vertical_lines += "<path d=\"M#{xc(cend)} #{yc(i)} L#{xc(cend)} #{$kmers_base}\" stroke=\"lightgrey\" />\n"
  
  sr_lines += "<text x=\"#{xc(cstart)}\" y=\"#{yc(i)}\" text-anchor=\"middle\" font-size=\"50%\">#{cstart}</text>\n"
  sr_lines += "<text x=\"#{xc(cend)}\" y=\"#{yc(i)}\" text-anchor=\"middle\" font-size=\"50%\">#{cend}</text>\n"
  sr_lines += "<path d=\"M#{xc(cstart)} #{yc(i) + 2} L#{xc(cend)} #{yc(i) + 2}\" stroke=\"black\" #{marker(ori == "F")}/>\n"
  midpoint = (cstart + cend) / 2
  sr_lines += "<text x=\"#{xc(midpoint)}\" y=\"#{yc(i)}\" text-anchor=\"middle\">#{unitig}</text>\n"
}

puts(<<EOS)
<svg width="#{($max_x - $min_x) + 1}pt\" height=\"#{($max_y - $min_y) + 1}pt"
          xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs
     id="defs4">
    <marker
       refX="0"
       refY="0"
       orient="auto"
       id="Arrow1Mstart"
       style="overflow:visible">
      <path
         d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
         transform="matrix(0.4,0,0,0.4,4,0)"
         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt" />
    </marker>
    <marker
       refX="0"
       refY="0"
       orient="auto"
       id="Arrow1Mend"
       style="overflow:visible">
      <path
         d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
         transform="matrix(-0.4,0,0,-0.4,-4,0)"
         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt" />
    </marker>
  </defs>
EOS


puts(vertical_lines, match_lines, kmer_lines, sr_lines)
puts('</svg>')
